// https://learn.microsoft.com/en-us/typography/opentype/spec/cmap

// Unicode platform
// Encoding ID 3 should be used in conjunction with 'cmap' subtable formats 4 or 6.
// almost format 4 same with windows
// Encoding ID 4 should be used in conjunction with subtable formats 10 or 12.
// Encoding ID 5 should only be used with a format 14 subtable.
// Encoding ID 6 should only be used in conjunction with 'cmap' subtable format 13

// Macintosh platform
// All cmap formats are supported by Apple platforms except 0, 8, and 10.
// almost format 6
// PingFangUI.ttc have only Unicode/4 Unicode/5

// Windows platform
// Fonts that support only Unicode BMP characters (U+0000 to U+FFFF) on the Windows platform must use encoding 1 with a format 4 subtable.
// Fonts that support Unicode supplementary-plane characters (U+10000 to U+10FFFF) on the Windows platform must use encoding 10 with a format 12 subtable.

// format 0 deprecated
// format 2 old font like STHeiti Macintosh/15,
// format 4 Unicode/3 and Windows/1
// format 6 Macintosh
// format 8 deprecated
// format 10 Unicode/4 and Windows/10,Macintosh not surport
// format 12 Unicode/4
// format 13 Unicode/6
// format 14 Unicode/5

use super::*;

#[derive(Clone)]
pub struct Table<'a> {
	pub version: u16, // Table version number (0).
	// pub num_tables: u16, // Number of encoding tables that follow.
	pub encoding_records: LazyArray16<'a, EncodingRecord>,

	pub data: &'a [u8],
}
impl<'a> Table<'a> {
	pub fn parse(data: &'a [u8]) -> Option<Self> {
		let mut s = Stream::new(data);
		let this = Self {
			version: s.read()?,
			encoding_records: s.parse_array16()?,
			data,
		};
		Some(this)
	}
	pub fn encoding(&'_ self, r: EncodingRecord) -> Option<SubTable<'_>> {
		SubTable::parse(self.data.get(r.subtable_offset.to_usize()..)?)
	}
}
impl<'a> core::fmt::Debug for Table<'a> {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		f.debug_struct("Table")
			.field("version", &self.version)
			.field("encoding_records", &self.encoding_records.len())
			.field(
				"encoding",
				&self.encoding_records.into_iter().map(|r| (r, self.encoding(r))).collect::<Vec<_>>(),
			)
			// .field("data", &self.data)
			.finish()
	}
}

#[derive(Debug, Clone, Copy)]
pub struct EncodingRecord {
	pub platform_id: u16,          // Platform ID.
	pub encoding_id: u16,          // Platform-specific encoding ID.
	pub subtable_offset: Offset32, // Byte offset from beginning of table to the subtable for this encoding.
}
impl FromData for EncodingRecord {
	const SIZE: usize = 8;
	#[inline]
	fn parse(data: &[u8]) -> Option<Self> {
		let mut s = Stream::new(data);
		let this = Self {
			platform_id: s.read()?,
			encoding_id: s.read()?,
			subtable_offset: s.read()?,
		};
		_ = PlatformEncoding::new(this.platform_id, this.encoding_id)?;
		Some(this)
	}
}

#[derive(Debug, Clone)]
pub enum SubTable<'a> {
	Format00(format00::Table<'a>),
	Format02(format02::Table<'a>),
	Format04(format04::Table<'a>),
	Format06(format06::Table<'a>),
	Format10(format10::Table<'a>),
	Format12(format12::Table<'a>),
	Format13(format13::Table<'a>),
	Format14(format14::Table<'a>),
}
impl<'a> SubTable<'a> {
	pub fn parse(data: &'a [u8]) -> Option<Self> {
		let format = Stream::read_at::<u16>(data, 0)?;
		Some(match format {
			0 => Self::Format00(format00::Table::parse(data)?),
			2 => Self::Format02(format02::Table::parse(data)?),
			4 => Self::Format04(format04::Table::parse(data)?),
			6 => Self::Format06(format06::Table::parse(data)?),
			10 => Self::Format10(format10::Table::parse(data)?),
			12 => Self::Format12(format12::Table::parse(data)?),
			13 => Self::Format13(format13::Table::parse(data)?),
			14 => Self::Format14(format14::Table::parse(data)?),
			_ => return None,
		})
	}
	pub fn glyph_index(&self, code_point: u32) -> Option<u16> {
		match self {
			SubTable::Format00(table) => table.glyph_index(code_point),
			SubTable::Format02(table) => table.glyph_index(code_point),
			SubTable::Format04(table) => table.glyph_index(code_point),
			SubTable::Format06(table) => table.glyph_index(code_point),
			SubTable::Format10(table) => table.glyph_index(code_point),
			SubTable::Format12(table) => table.glyph_index(code_point),
			SubTable::Format13(table) => table.glyph_index(code_point),
			SubTable::Format14(_table) => None,
		}
	}
	pub fn codepoints(&self, f: impl FnMut(u32, u16)) {
		match self {
			SubTable::Format00(table) => table.codepoints(f),
			SubTable::Format02(table) => table.codepoints(f),
			SubTable::Format04(table) => table.codepoints(f),
			SubTable::Format06(table) => table.codepoints(f),
			SubTable::Format10(table) => table.codepoints(f),
			SubTable::Format12(table) => table.codepoints(f),
			SubTable::Format13(table) => table.codepoints(f),
			SubTable::Format14(_table) => {}
		}
	}
	pub fn codepoints_variation(&self, f: impl FnMut(u32, u32, GlyphVariation)) {
		match self {
			SubTable::Format14(table) => table.codepoints(f),
			_ => {}
		}
	}
	pub fn language(&self, platform: u16) -> Option<u16> {
		// All 'cmap' subtable formats include a language field.
		// The language field must be set to zero for all 'cmap' subtables whose platform IDs are other than Macintosh (platform ID 1).
		// For 'cmap' subtables whose platform IDs are Macintosh, set this field to the Macintosh language ID of the 'cmap' subtable plus one,
		// or to zero if the 'cmap' subtable is not language-specific.
		let language = match self {
			SubTable::Format00(table) => table.language,
			SubTable::Format02(table) => table.language,
			SubTable::Format04(table) => table.language,
			SubTable::Format06(table) => table.language,
			SubTable::Format10(table) => u16::try_from(table.language).ok()?,
			SubTable::Format12(table) => u16::try_from(table.language).ok()?,
			SubTable::Format13(table) => u16::try_from(table.language).ok()?,
			SubTable::Format14(_) => return None,
		};
		if platform == 1 {
			if language > 0 {
				return Some(language - 1);
			}
		} else {
			debug_assert_eq!(language, 0);
		}
		None
	}
}

#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum GlyphVariation {
	/// Glyph was found in the variation encoding table.
	Found(u16),
	/// Glyph should be looked in other, non-variation tables.
	///
	/// Basically, you should use `Encoding::glyph_index` or `Face::glyph_index`
	/// in this case.
	UseDefault,
}

pub mod format00 {
	// https://learn.microsoft.com/en-us/typography/opentype/spec/cmap#format-0-byte-encoding-table
	use super::*;

	#[derive(Debug, Clone)]
	pub struct Table<'a> {
		pub format: u16,
		pub length: u16,
		pub language: u16,
		pub glyph_id_array: LazyArray16<'a, u8>, // An array that maps character codes to glyph index values.
	}
	impl<'a> Table<'a> {
		pub fn parse(data: &'a [u8]) -> Option<Self> {
			let mut s = Stream::new(data);
			let this = Self {
				format: s.read()?,
				length: s.read()?,
				language: s.read()?,
				glyph_id_array: s.read_array16(256)?,
			};
			Some(this)
		}
		pub fn glyph_index(&self, code_point: u32) -> Option<u16> {
			let glyph_id = self.glyph_id_array.get(u16::try_from(code_point).ok()?)?;
			if glyph_id != 0 {
				Some(u16::from(glyph_id))
			} else {
				None
			}
		}
		pub fn codepoints(&self, mut f: impl FnMut(u32, u16)) {
			for (i, glyph_id) in self.glyph_id_array.into_iter().enumerate() {
				if glyph_id != 0 {
					f(i as u32, u16::from(glyph_id));
				}
			}
		}
	}
}
pub mod format02 {
	// https://learn.microsoft.com/en-us/typography/opentype/spec/cmap#format-2-high-byte-mapping-through-table
	use super::*;

	#[derive(Clone)]
	pub struct Table<'a> {
		pub format: u16,
		pub length: u16,
		pub language: u16,
		pub sub_header_keys: LazyArray16<'a, u16>, // Array that maps high bytes into the subHeaders array: value is subHeaders index × 8.
		pub sub_headers: LazyArray16<'a, SubHeader>, // Variable-length array of SubHeader records.
		// uint16	glyphIdArray[ ]	Variable-length array containing sub-arrays used for mapping the low byte of 2-byte characters.
		pub sub_headers_offset: usize,
		pub data: &'a [u8],
	}
	impl<'a> Table<'a> {
		pub fn parse(data: &'a [u8]) -> Option<Self> {
			let mut s = Stream::new(data);
			let format: u16 = s.read()?;
			let length: u16 = s.read()?;
			let language: u16 = s.read()?;
			let sub_header_keys = s.read_array16(256)?;
			let sub_headers_count = sub_header_keys.into_iter().map(|n| n / 8).max()? + 1;
			let sub_headers_offset = s.offset();
			let sub_headers = s.read_array16::<SubHeader>(sub_headers_count)?;
			let this = Self {
				format,
				length,
				language,
				sub_header_keys,
				sub_headers,
				sub_headers_offset,
				data,
			};
			Some(this)
		}
		pub fn glyph_index(&self, code_point: u32) -> Option<u16> {
			// This subtable supports code points only in a u16 range.
			let code_point = u16::try_from(code_point).ok()?;
			let high_byte = code_point >> 8;
			let low_byte = code_point & 0x00FF;

			let i = if code_point < 0xff {
				// 'SubHeader 0 is special: it is used for single-byte character codes.'
				0
			} else {
				// 'Array that maps high bytes to subHeaders: value is subHeader index × 8.'
				self.sub_header_keys.get(high_byte)? / 8
			};

			let sub_header = self.sub_headers.get(i)?;

			let first_code = sub_header.first_code;
			let range_end = first_code.checked_add(sub_header.entry_count)?;
			if low_byte < first_code || low_byte >= range_end {
				return None;
			}
			// SubHeaderRecord::id_range_offset points to SubHeaderRecord::first_code
			// in the glyphIndexArray. So we have to advance to our code point.
			let index_offset = low_byte.checked_sub(first_code)? * 2;
			self.get_glyph(i, index_offset, &sub_header)
		}
		fn get_glyph(&self, idx: u16, index_offset: u16, sub_header: &SubHeader) -> Option<u16> {
			// 'The value of the idRangeOffset is the number of bytes
			// past the actual location of the idRangeOffset'.
			let offset = self.sub_headers_offset
                // Advance to required subheader.
                + SubHeader::SIZE * usize::from(idx + 1)
                // Move back to idRangeOffset start.
                - u16::SIZE
                // Use defined offset.
                + usize::from(sub_header.id_range_offset)
                // Advance to required index in the glyphIndexArray.
                + usize::from( index_offset);

			let glyph: u16 = Stream::read_at(self.data, offset)?;
			if glyph == 0 {
				return None;
			}

			u16::try_from((i32::from(glyph) + i32::from(sub_header.id_delta)) % 65536).ok()
		}
		pub fn codepoints(&self, f: impl FnMut(u32, u16)) {
			_ = self.codepoints_inner(f);
		}
		#[inline]
		fn codepoints_inner(&self, mut f: impl FnMut(u32, u16)) -> Option<()> {
			for high_byte in 0u16..256 {
				let idx = self.sub_header_keys.get(high_byte)? / 8;
				let sub_header = self.sub_headers.get(idx)?;
				let first_code = sub_header.first_code;
				if idx == 0 {
					if high_byte == 0 {
						for k in 0..sub_header.entry_count {
							let code_point = first_code.checked_add(k)?;
							if let Some(glyph) = self.get_glyph(idx, k * 2, &sub_header) {
								f(u32::from(code_point), glyph);
							}
						}
					}
				} else {
					let base = first_code.checked_add(high_byte << 8)?;
					for k in 0..sub_header.entry_count {
						let code_point = base.checked_add(k)?;
						if let Some(glyph) = self.get_glyph(idx, k * 2, &sub_header) {
							f(u32::from(code_point), glyph);
						}
					}
				}
			}
			// for first_byte in 0u16..256 {
			// 	let i = self.sub_header_keys.get(first_byte)? / 8;
			// 	let sub_header = self.sub_headers.get(i)?;
			// 	let first_code = sub_header.first_code;
			// 	if i == 0 {
			// 		// This is a single byte code.
			// 		let range_end = first_code.checked_add(sub_header.entry_count)?;
			// 		if first_byte >= first_code && first_byte < range_end {
			// 			f(u32::from(first_byte), 0);
			// 		}
			// 	} else {
			// 		// This is a two byte code.
			// 		let base = first_code.checked_add(first_byte << 8)?;
			// 		for k in 0..sub_header.entry_count {
			// 			let code_point = base.checked_add(k)?;
			// 			f(u32::from(code_point), 0);
			// 		}
			// 	}
			// }
			Some(())
		}
	}
	impl<'a> core::fmt::Debug for Table<'a> {
		fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
			f.debug_struct("Table02")
				// .field("format", &self.format)
				.field("length", &self.length)
				.field("language", &self.language)
				.field("sub_header_keys", &self.sub_header_keys.len())
				.field("sub_headers", &self.sub_headers.len())
				.field("sub_headers_offset", &self.sub_headers_offset)
				// .field("data", &self.data)
				.finish()?;
			// self.codepoints(|code_point, glyph| {
			// 	write!(f, "[{:06X} {}]", code_point, glyph).ok();
			// });
			Ok(())
		}
	}

	#[derive(Debug, Clone, Copy)]
	pub struct SubHeader {
		pub first_code: u16,  // First valid low byte for this SubHeader.
		pub entry_count: u16, // Number of valid low bytes for this SubHeader.
		pub id_delta: i16,
		pub id_range_offset: u16,
	}
	impl FromData for SubHeader {
		const SIZE: usize = 8;
		#[inline]
		fn parse(data: &[u8]) -> Option<Self> {
			let mut s = Stream::new(data);
			Some(SubHeader {
				first_code: s.read::<u16>()?,
				entry_count: s.read::<u16>()?,
				id_delta: s.read::<i16>()?,
				id_range_offset: s.read::<u16>()?,
			})
		}
	}
}
pub mod format04 {
	// https://learn.microsoft.com/en-us/typography/opentype/spec/cmap#format-4-segment-mapping-to-delta-values
	// Fonts that support only Unicode BMP characters (U+0000 to U+FFFF) on the Windows platform must use encoding 1 with a format 4 subtable.
	// Unicode platform Encoding ID 3 should be used in conjunction with 'cmap' subtable formats 4 or 6.
	use std::i16;

	use super::*;

	#[derive(Clone)]
	pub struct Table<'a> {
		pub format: u16,                            // Format number is set to 4.
		pub length: u16,                            // This is the length in bytes of the subtable.
		pub language: u16, // For requirements on use of the language field, see “Use of the language field in 'cmap' subtables” in this document.
		pub seg_count_x2: u16, // 2 × segCount.
		pub search_range: u16, // Maximum power of 2 less than or equal to segCount, times 2 ((2**floor(log2(segCount))) * 2, where “**” is an exponentiation operator)
		pub entry_selector: u16, // Log2 of the maximum power of 2 less than or equal to segCount (log2(searchRange/2), which is equal to floor(log2(segCount)))
		pub range_shift: u16,    // segCount times 2, minus searchRange ((segCount * 2) - searchRange)
		pub end_codes: LazyArray16<'a, u16>, // End characterCode for each segment, last=0xFFFF.
		pub reserved_pad: u16,   //	Set to 0.
		pub start_codes: LazyArray16<'a, u16>, // Start character code for each segment.
		pub id_deltas: LazyArray16<'a, i16>, // Delta for all character codes in segment.
		pub id_range_offsets: LazyArray16<'a, u16>, // Offsets into glyphIdArray or 0
		// uint16	glyphIdArray[ ]	Glyph index array (arbitrary length)
		pub seg_count: u16,
		pub id_range_offset_pos: usize,
		pub data: &'a [u8],
	}
	impl<'a> Table<'a> {
		pub fn parse(data: &'a [u8]) -> Option<Self> {
			let mut s = Stream::new(data);
			let format = s.read()?;
			let length = s.read()?;
			let language = s.read()?;
			let seg_count_x2 = s.read()?;
			if seg_count_x2 < 2 {
				return None;
			}
			let seg_count = seg_count_x2 >> 1;
			let this = Self {
				format,
				length,
				language,
				seg_count_x2,
				seg_count,
				search_range: s.read()?,
				entry_selector: s.read()?,
				range_shift: s.read()?,
				end_codes: s.read_array16(seg_count)?,
				reserved_pad: s.read()?,
				start_codes: s.read_array16(seg_count)?,
				id_deltas: s.read_array16(seg_count)?,
				id_range_offset_pos: s.offset(),
				id_range_offsets: s.read_array16(seg_count)?,
				data,
			};
			Some(this)
		}
		pub fn codepoints(&self, f: impl FnMut(u32, u16)) {
			_ = self.codepoints_(f);
		}
		#[inline]
		fn codepoints_(&self, mut f: impl FnMut(u32, u16)) -> Option<()> {
			for (seg, (start, end)) in self.start_codes.into_iter().zip(self.end_codes).enumerate() {
				let seg = u16::try_from(seg).ok()?;
				// OxFFFF value is special and indicates codes end.
				if start == end && start == 0xFFFF {
					break;
				}
				for code_point in start..=end {
					if let Some(glyph) = self.get_glyph(seg, start, code_point) {
						f(u32::from(code_point), glyph);
					}
				}
			}
			None
		}
		pub fn glyph_index(&self, code_point: u32) -> Option<u16> {
			let code_point = u16::try_from(code_point).ok()?;
			let mut start = 0;
			let mut end = self.start_codes.len();
			while end > start {
				let index = (start + end) / 2;
				let end_value = self.end_codes.get(index)?;
				if end_value >= code_point {
					let start_value = self.start_codes.get(index)?;
					if start_value <= code_point {
						return self.get_glyph(index, start_value, code_point);
					} else {
						end = index;
					}
				} else {
					start = index + 1;
				}
			}
			None
		}
		fn get_glyph(&self, seg: u16, start: u16, code_point: u16) -> Option<u16> {
			let id_offset = self.id_range_offsets.get(seg)?;
			let id_offset_pos = (self.id_range_offset_pos + usize::from(seg) * 2) as u16;
			let id_delta = self.id_deltas.get(seg).unwrap();
			if id_offset == 0 {
				Some(code_point.wrapping_add(id_delta as u16))
			} else if id_offset == 0xFFFF {
				None
			} else {
				let delta = (u32::from(code_point) - u32::from(start)) * 2;
				let delta = u16::try_from(delta).ok()?;
				let pos = id_offset_pos.wrapping_add(delta);
				let pos = pos.wrapping_add(id_offset);
				let glyph_v: u16 = Stream::read_at(self.data, usize::from(pos))?;
				// 0 indicates missing glyph.
				if glyph_v == 0 {
					return None;
				}
				let glyph_id = (glyph_v as i16).wrapping_add(id_delta);
				u16::try_from(glyph_id).ok()
			}
		}
	}
	impl<'a> core::fmt::Debug for Table<'a> {
		fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
			f.debug_struct("Table04")
				// .field("format", &self.format)
				.field("length", &self.length)
				.field("language", &self.language)
				.field("seg_count_x2", &self.seg_count_x2)
				.field("search_range", &self.search_range)
				.field("entry_selector", &self.entry_selector)
				.field("range_shift", &self.range_shift)
				// .field("end_code", &self.end_code)
				// .field("reserved_pad", &self.reserved_pad)
				// .field("start_code", &self.start_code)
				// .field("id_delta", &self.id_delta)
				// .field("id_range_offset", &self.id_range_offset)
				// .field("seg_count", &self.seg_count)
				// .field("id_range_offset_pos", &self.id_range_offset_pos)
				.finish()?;
			// self.codepoints(|code_point, glyph| {
			// 	write!(f, "[{:06X} {}]", code_point, glyph).ok();
			// });
			Ok(())
		}
	}
}
pub mod format06 {
	// https://learn.microsoft.com/en-us/typography/opentype/spec/cmap#format-6-trimmed-table-mapping
	// Unicode platform Encoding ID 3 should be used in conjunction with 'cmap' subtable formats 4 or 6.
	// used in Macintosh platform
	use super::*;

	#[derive(Clone)]
	pub struct Table<'a> {
		pub format: u16,     // Format number is set to 6.
		pub length: u16,     // This is the length in bytes of the subtable.
		pub language: u16, // For requirements on use of the language field, see “Use of the language field in 'cmap' subtables” in this document.
		pub first_code: u16, // First character code of subrange.
		// pub entry_count: u16, // Number of character codes in subrange.
		pub glyph_id_array: LazyArray16<'a, u16>, // Array of glyph index values for character codes in the range.
	}
	impl<'a> Table<'a> {
		pub fn parse(data: &'a [u8]) -> Option<Self> {
			let mut s = Stream::new(data);
			let this = Self {
				format: s.read()?,
				length: s.read()?,
				language: s.read()?,
				first_code: s.read()?,
				glyph_id_array: s.parse_array16()?,
			};
			Some(this)
		}
		pub fn codepoints(&self, mut f: impl FnMut(u32, u16)) {
			for i in 0..self.glyph_id_array.len() {
				if let Some(code_point) = self.first_code.checked_add(i) {
					if let Some(glyph) = self.glyph_id_array.get(i) {
						f(u32::from(code_point), glyph);
					}
				}
			}
		}
		pub fn glyph_index(&self, code_point: u32) -> Option<u16> {
			let code_point = u16::try_from(code_point).ok()?;
			let idx = code_point.checked_sub(self.first_code)?;
			self.glyph_id_array.get(idx)
		}
	}
	impl<'a> core::fmt::Debug for Table<'a> {
		fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
			f.debug_struct("Table06")
				// .field("format", &self.format)
				.field("length", &self.length)
				.field("language", &self.language)
				.field("first_code", &self.first_code)
				.field("glyph_id_array", &self.glyph_id_array.len())
				.finish()?;
			// self.codepoints(|code_point, glyph| {
			// 	write!(f, "[{:06X} {}]", code_point, glyph).ok();
			// });
			Ok(())
		}
	}
}
pub mod format10 {
	// https://learn.microsoft.com/en-us/typography/opentype/spec/cmap#format-10-trimmed-array
	// Unicode platform Encoding ID 4 should be used in conjunction with subtable formats 10 or 12.
	use super::*;

	#[derive(Clone)]
	pub struct Table<'a> {
		pub format: u16,          // Subtable format; set to 10.
		pub reserved: u16,        // Reserved; set to 0
		pub length: u32,          // Byte length of this subtable (including the header)
		pub language: u32, // For requirements on use of the language field, see “Use of the language field in 'cmap' subtables” in this document.
		pub start_char_code: u32, // First character code covered
		// pub num_chars: u32, // Number of character codes covered
		pub glyph_id_array: LazyArray32<'a, u16>, // Array of glyph indices for the character codes covered
	}
	impl<'a> Table<'a> {
		pub fn parse(data: &'a [u8]) -> Option<Self> {
			let mut s = Stream::new(data);
			let this = Self {
				format: s.read()?,
				reserved: s.read()?,
				length: s.read()?,
				language: s.read()?,
				start_char_code: s.read()?,
				glyph_id_array: s.parse_array32()?,
			};
			Some(this)
		}
		pub fn glyph_index(&self, code_point: u32) -> Option<u16> {
			let idx = code_point.checked_sub(self.start_char_code)?;
			self.glyph_id_array.get(idx)
		}
		pub fn codepoints(&self, mut f: impl FnMut(u32, u16)) {
			for i in 0..self.glyph_id_array.len() {
				if let Some(code_point) = self.start_char_code.checked_add(i) {
					if let Some(glyph) = self.glyph_id_array.get(i) {
						f(code_point, glyph);
					}
				}
			}
		}
	}
	impl<'a> core::fmt::Debug for Table<'a> {
		fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
			f.debug_struct("Table10")
				// .field("format", &self.format)
				.field("reserved", &self.reserved)
				.field("length", &self.length)
				.field("language", &self.language)
				.field("start_char_code", &self.start_char_code)
				.field("glyph_id_array", &self.glyph_id_array.len())
				.finish()?;
			// self.codepoints(|code_point, glyph| {
			// 	write!(f, "[{:06X} {}]", code_point, glyph).ok();
			// });
			Ok(())
		}
	}
}
pub mod format12 {
	// https://learn.microsoft.com/en-us/typography/opentype/spec/cmap#format-12-segmented-coverage
	// Windows platform Fonts that support Unicode supplementary-plane characters (U+10000 to U+10FFFF) on the Windows platform must use encoding 10 with a format 12 subtable.
	// Unicode platform Encoding ID 4 should be used in conjunction with subtable formats 10 or 12.
	use super::*;

	#[derive(Clone)]
	pub struct Table<'a> {
		pub format: u16,   // Subtable format; set to 12.
		pub reserved: u16, // Reserved; set to 0
		pub length: u32,   // Byte length of this subtable (including the header)
		pub language: u32, // For requirements on use of the language field, see “Use of the language field in 'cmap' subtables” in this document.
		// pub num_groups: u32, // Number of groupings which follow
		pub groups: LazyArray32<'a, SequentialMapGroup>, // Array of SequentialMapGroup records.
	}
	impl<'a> Table<'a> {
		pub fn parse(data: &'a [u8]) -> Option<Self> {
			let mut s = Stream::new(data);
			let this = Self {
				format: s.read()?,
				reserved: s.read()?,
				length: s.read()?,
				language: s.read()?,
				groups: s.parse_array32()?,
			};
			Some(this)
		}
		pub fn glyph_index(&self, code_point: u32) -> Option<u16> {
			for group in self.groups {
				let start_char_code = group.start_char_code;
				if code_point >= start_char_code && code_point <= group.end_char_code {
					let id = group.start_glyph_id.checked_add(code_point)?.checked_sub(group.start_char_code)?;
					return u16::try_from(id).ok();
				}
			}
			None
		}
		pub fn codepoints(&self, mut f: impl FnMut(u32, u16)) {
			for group in self.groups {
				for code_point in group.start_char_code..=group.end_char_code {
					if let Some(glyph) = code_point
						.checked_sub(group.start_char_code)
						.and_then(|v| v.checked_add(group.start_glyph_id))
						.and_then(|v| u16::try_from(v).ok())
					{
						f(code_point, glyph);
					}
				}
			}
		}
	}
	impl<'a> core::fmt::Debug for Table<'a> {
		fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
			f.debug_struct("Table12")
				// .field("format", &self.format)
				// .field("reserved", &self.reserved)
				.field("length", &self.length)
				.field("language", &self.language)
				.field("groups", &self.groups.len())
				.finish()?;
			// self.codepoints(|code_point, glyph| {
			// 	write!(f, "[{:06X} {}]", code_point, glyph).ok();
			// });
			Ok(())
		}
	}

	#[derive(Debug, Clone, Copy)]
	pub struct SequentialMapGroup {
		pub start_char_code: u32, //First character code in this group
		pub end_char_code: u32,   //Last character code in this group
		pub start_glyph_id: u32,  //Glyph index corresponding to the starting character code
	}
	impl FromData for SequentialMapGroup {
		const SIZE: usize = 12;
		#[inline]
		fn parse(data: &[u8]) -> Option<Self> {
			let mut s = Stream::new(data);
			Some(SequentialMapGroup {
				start_char_code: s.read::<u32>()?,
				end_char_code: s.read::<u32>()?,
				start_glyph_id: s.read::<u32>()?,
			})
		}
	}
}
pub mod format13 {
	// https://learn.microsoft.com/en-us/typography/opentype/spec/cmap#format-13-many-to-one-range-mappings
	// used for Unicode platform Encoding ID 6	Unicode full repertoire—for use with subtable format 13

	use super::*;

	#[derive(Clone)]
	pub struct Table<'a> {
		pub format: u16,   // Subtable format; set to 13.
		pub reserved: u16, // Reserved; set to 0
		pub length: u32,   // Byte length of this subtable (including the header)
		pub language: u32, // For requirements on use of the language field, see “Use of the language field in 'cmap' subtables” in this document.
		// pub num_groups: u32, // Number of groupings which follow
		pub groups: LazyArray32<'a, ConstantMapGroup>, // Array of ConstantMapGroup records.
	}
	impl<'a> Table<'a> {
		pub fn parse(data: &'a [u8]) -> Option<Self> {
			let mut s = Stream::new(data);
			let this = Self {
				format: s.read()?,
				reserved: s.read()?,
				length: s.read()?,
				language: s.read()?,
				groups: s.parse_array32()?,
			};
			Some(this)
		}
		pub fn glyph_index(&self, code_point: u32) -> Option<u16> {
			for group in self.groups {
				let start_char_code = group.start_char_code;
				if code_point >= start_char_code && code_point <= group.end_char_code {
					return u16::try_from(group.glyph_id).ok();
				}
			}
			None
		}
		pub fn codepoints(&self, mut f: impl FnMut(u32, u16)) {
			for group in self.groups {
				for code_point in group.start_char_code..=group.end_char_code {
					if let Ok(glyph) = u16::try_from(group.glyph_id) {
						f(code_point, glyph);
					}
				}
			}
		}
	}
	impl<'a> core::fmt::Debug for Table<'a> {
		fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
			f.debug_struct("Table13")
				// .field("format", &self.format)
				// .field("reserved", &self.reserved)
				.field("length", &self.length)
				.field("language", &self.language)
				.field("groups", &self.groups.len())
				.finish()?;
			// self.codepoints(|code_point, glyph| {
			// 	write!(f, "[{:06X} {}]", code_point, glyph).ok();
			// });
			Ok(())
		}
	}

	#[derive(Debug, Clone, Copy)]
	pub struct ConstantMapGroup {
		pub start_char_code: u32, // First character code in this group
		pub end_char_code: u32,   // Last character code in this group
		pub glyph_id: u32,        // Glyph index to be used for all the characters in the group’s range.
	}
	impl FromData for ConstantMapGroup {
		const SIZE: usize = 12;
		#[inline]
		fn parse(data: &[u8]) -> Option<Self> {
			let mut s = Stream::new(data);
			Some(ConstantMapGroup {
				start_char_code: s.read::<u32>()?,
				end_char_code: s.read::<u32>()?,
				glyph_id: s.read::<u32>()?,
			})
		}
	}
}
pub mod format14 {
	// https://learn.microsoft.com/en-us/typography/opentype/spec/cmap#format-14-unicode-variation-sequences
	// used for Unicode platform Encoding ID 5 Unicode variation sequences—for use with subtable format 14

	use super::*;

	#[derive(Clone)]
	pub struct Table<'a> {
		pub format: u16, // Subtable format; set to 14.
		pub length: u32, // Byte length of this subtable (including this header)
		// pub num_var_selector_records: u32,                    // Number of variation Selector Records
		pub var_selector: LazyArray32<'a, VariationSelector>, // Array of VariationSelector records.

		pub data: &'a [u8],
	}
	impl<'a> Table<'a> {
		pub fn parse(data: &'a [u8]) -> Option<Self> {
			let mut s = Stream::new(data);
			let this = Self {
				format: s.read()?,
				length: s.read()?,
				var_selector: s.parse_array32()?,
				data,
			};
			Some(this)
		}
		pub fn get_selector_default_uvs(&self, r: VariationSelector) -> Option<DefaultUVS<'a>> {
			DefaultUVS::parse(self.data.get(r.default_uvs_offset?.to_usize()..)?)
		}
		pub fn get_selector_non_default_uvs(&self, r: VariationSelector) -> Option<NonDefaultUVS<'a>> {
			NonDefaultUVS::parse(self.data.get(r.non_default_uvs_offset?.to_usize()..)?)
		}
		pub fn glyph_index(&self, code_point: u32, variation: u32) -> Option<GlyphVariation> {
			for s in self.var_selector {
				if s.var_selector == variation {
					if let Some(default) = self.get_selector_default_uvs(s) {
						for r in default.ranges {
							let end = r.start_unicode_value.0 + u32::from(r.additional_count);
							if (r.start_unicode_value.0..=end).contains(&code_point) {
								return Some(GlyphVariation::UseDefault);
							}
						}
					}
					if let Some(non_default) = self.get_selector_non_default_uvs(s) {
						for r in non_default.uvs_mappings {
							if r.unicode_value.0 == code_point {
								return Some(GlyphVariation::Found(r.glyph_id));
							}
						}
					}
				}
			}
			None
		}
		pub fn codepoints(&self, mut f: impl FnMut(u32, u32, GlyphVariation)) {
			for s in self.var_selector {
				if let Some(default) = self.get_selector_default_uvs(s) {
					for r in default.ranges {
						let end = r.start_unicode_value.0 + u32::from(r.additional_count);
						for cp in r.start_unicode_value.0..=end {
							f(cp, s.var_selector, GlyphVariation::UseDefault);
						}
					}
				}
				if let Some(non_default) = self.get_selector_non_default_uvs(s) {
					for r in non_default.uvs_mappings {
						f(r.unicode_value.0, s.var_selector, GlyphVariation::Found(r.glyph_id));
					}
				}
			}
		}
	}
	impl<'a> core::fmt::Debug for Table<'a> {
		fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
			f.debug_struct("Table14")
				// .field("format", &self.format)
				.field("length", &self.length)
				.field("var_selector", &self.var_selector.len())
				// .field("data", &self.data)
				.finish()
		}
	}

	#[derive(Debug, Clone, Copy)]
	pub struct VariationSelector {
		pub var_selector: u32,                        // Variation selector
		pub default_uvs_offset: Option<Offset32>,     // Offset from the start of the format 14 subtable to Default UVS table. May be 0.
		pub non_default_uvs_offset: Option<Offset32>, // Offset from the start of the format 14 subtable to Non-Default UVS table. May be 0.
	}
	impl FromData for VariationSelector {
		const SIZE: usize = 11;
		#[inline]
		fn parse(data: &[u8]) -> Option<Self> {
			let mut s = Stream::new(data);
			Some(VariationSelector {
				var_selector: s.read::<U24>()?.0,
				default_uvs_offset: s.read::<Option<Offset32>>()?,
				non_default_uvs_offset: s.read::<Option<Offset32>>()?,
			})
		}
	}

	#[derive(Debug, Clone)]
	pub struct DefaultUVS<'a> {
		// pub num_unicode_value_ranges: u32,         // Number of Unicode character ranges.
		pub ranges: LazyArray32<'a, UnicodeRange>, // Array of UnicodeRange records.
	}
	impl<'a> DefaultUVS<'a> {
		pub fn parse(data: &'a [u8]) -> Option<Self> {
			let mut s = Stream::new(data);
			Some(Self {
				ranges: s.parse_array32()?,
			})
		}
	}

	#[derive(Debug, Clone, Copy)]
	pub struct UnicodeRange {
		pub start_unicode_value: U24, // First value in this range
		pub additional_count: u8,     // Number of additional values in this range
	}
	impl FromData for UnicodeRange {
		const SIZE: usize = 4;
		fn parse(data: &[u8]) -> Option<Self> {
			let mut s = Stream::new(data);
			Some(Self {
				start_unicode_value: s.read()?,
				additional_count: s.read()?,
			})
		}
	}

	#[derive(Debug, Clone)]
	pub struct NonDefaultUVS<'a> {
		// pub num_uvsmappings: u32,                      // Number of UVS Mappings that follow
		pub uvs_mappings: LazyArray32<'a, UVSMapping>, // Array of UVSMapping records.
	}
	impl<'a> NonDefaultUVS<'a> {
		pub fn parse(data: &'a [u8]) -> Option<Self> {
			let mut s = Stream::new(data);
			Some(Self {
				uvs_mappings: { s.parse_array32()? },
			})
		}
	}

	#[derive(Debug, Clone, Copy)]
	pub struct UVSMapping {
		pub unicode_value: U24, // Base Unicode value of the UVS
		pub glyph_id: u16,      // Glyph ID of the UVS
	}
	impl FromData for UVSMapping {
		const SIZE: usize = 5;
		fn parse(data: &[u8]) -> Option<Self> {
			let mut s = Stream::new(data);
			Some(Self {
				unicode_value: s.read()?,
				glyph_id: s.read()?,
			})
		}
	}
}
